#include <functional>
#include <vector>
#include <string>
#include <stdio.h>
#include <unistd.h>

#include "thread/BlockingQueue.h"
#include "thread/Latch.h"
#include "thread/Thread.h"
#include "thread/current_thread.h"

class Test
{
 public:
  Test(int numThreads)
    : latch_(numThreads),
      threads_{}
  {
    for (int i = 0; i < numThreads; ++i)
    {
      std::string name = "work thread " +  std::to_string(i);
      threads_.push_back(
        std::make_unique<Thread>(std::bind(&Test::threadFunc, this), name));
    }
    for (const auto &t : threads_)
      t->start();
  }

  void run(int times)
  {
    printf("waiting for count down latch\n");
    latch_.wait();
    printf("all threads started\n");
    for (int i = 0; i < times; ++i)
    {
      char buf[32];
      snprintf(buf, sizeof buf, "hello %d", i);
      queue_.push(buf);
      printf("tid=%d, put data = %s, size = %zd\n", 
              CurrentThread::tid(), buf, queue_.size());
    }
  }

  void joinAll()
  {
    for (size_t i = 0; i < threads_.size(); ++i)
    {
      queue_.push("stop");
    }
    for (const auto &t : threads_)
      t->join();
  }

 private:

  void threadFunc()
  {
    printf("tid=%d, %s started\n",
           CurrentThread::tid(),
           CurrentThread::thread_name());

    latch_.count_down();
    bool running = true;
    while (running)
    {
      std::string d(queue_.pop());
      printf("tid=%d, get data = %s, size = %zd\n", CurrentThread::tid(), d.c_str(), queue_.size());
      running = (d != "stop");
    }

    printf("tid=%d, %s stopped\n",
           CurrentThread::tid(),
           CurrentThread::thread_name());
  }

  BlockingQueue<std::string> queue_;
  Latch latch_;
  std::vector<std::unique_ptr<Thread>> threads_;
};

int main()
{
  printf("pid=%d, tid=%d\n", ::getpid(), CurrentThread::tid());
  Test t(5);
  t.run(100);
  t.joinAll();

  printf("number of created threads %d\n", Thread::TOTAL().load());
}
